package com.example.eventbusdemo;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author wdl
 */
public class EventBus {

    /**
     * 存储对象的方法集合
     */
    private Map<Object, List<SubscribeMethod>> cacheMap;

    /**
     * Handler对象用于线程切换
     */
    private Handler mHandler = new Handler();

    /**
     * 单例模式
     */
    public static volatile EventBus instance;

    private EventBus() {
        cacheMap = new HashMap<>();
    }

    public static EventBus getDefault() {
        if (instance == null) {
            synchronized (EventBus.class) {
                if (instance == null) {
                    instance = new EventBus();
                }
            }
        }
        return instance;
    }

    /**
     * 注册对象
     * 将注册对象中标记了Subscribe的方法加到数据Map中进行管理
     *
     * @param object 对象
     */
    public void register(Object object) {
        //需找Object中所有标记为Subscribe的方法放入map中管理
        List<SubscribeMethod> list = cacheMap.get(object);
        if (list == null) {
            list = findSubscribeMethods(object);
            cacheMap.put(object, list);
        }
    }

    /**
     * 寻找对象中标记Subscribe的方法集合
     *
     * @param object 对象
     * @return 标记方法的集合
     */
    private List<SubscribeMethod> findSubscribeMethods(Object object) {
        List<SubscribeMethod> list = new ArrayList<>();
        Class<?> clazz = object.getClass();
        while (clazz != null) {
            //凡是系统级别的父类肯定不包含Subscribe注解可以直接省略,这里是优化
            String name = clazz.getName();
            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
                break;
            }

            //获取当前类的方法（区别于getMethods()获取当前类及其父类的方法）
            Method[] methods = clazz.getDeclaredMethods();
            //遍历方法集合需找标记方法
            for (Method method :
                    methods) {
                //找到注解为Subscribe的方法
                Subscribe subscribe = method.getAnnotation(Subscribe.class);
                //如果没有Subscribe注解则跳过这个方法
                if (subscribe == null) {
                    continue;
                }
                //有Subscribe注解的方法还需要获取方法的参数
                //判断方法中的参数类型和个数
                Class<?>[] types = method.getParameterTypes();
                //参数个数不为1（这边参数的个数是根据自己代码设定的这里1更加方便判断）则说明错误
                if (types.length != 1) {
                    Log.e("Error", "参数出错");
                    continue;
                }
                //获取当前方法的线程模式
                ThreadMode threadMode = subscribe.threadMode();
                //创建方法的封装对象
                SubscribeMethod subscribeMethod = new SubscribeMethod(method, threadMode, types[0]);
                //添加方法
                list.add(subscribeMethod);
            }
            //寻找父类父类中的方法
            clazz = clazz.getSuperclass();
        }
        return list;
    }

    /**
     * 执行某方法
     *
     * @param type 参数
     */
    public void post(final Object type) {
        //循环cacheMap里的方法，调用对应的方法
        Set<Object> set = cacheMap.keySet();
        Iterator<Object> iterator = set.iterator();
        while (iterator.hasNext()) {
            final Object object = iterator.next();
            List<SubscribeMethod> list = cacheMap.get(object);
            //根据传递参数的类型来调用方法
            for (final SubscribeMethod subscribeMethod :
                    list) {
                //通过参数类型比较来确定调用的方法
                if (subscribeMethod.getType().isAssignableFrom(type.getClass())) {
                    //根据ThreadMode设置的线程模式来切换线程
                    switch (subscribeMethod.getThreadMode()) {
                        case MAIN:
                            //主线程->主线程
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                invoke(subscribeMethod, object, type);
                            } else {
                                //子线程->主线程
                                mHandler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(subscribeMethod, object, type);
                                    }
                                });
                            }
                            break;
                        case BACKGORUND:
                            //子线程->子线程
                            invoke(subscribeMethod, object, type);
                            //主线程->子线程
//                            ExecutorService
                            break;
                        default:
                    }

                }
            }
        }
    }

    /**
     * 执行方法
     *
     * @param subscribeMethod 封装的方法对象
     * @param object          该方法对应的对象
     * @param type            参数
     */
    private void invoke(SubscribeMethod subscribeMethod, Object object, Object type) {
        Method method = subscribeMethod.getMethod();
        try {
            method.invoke(object, type);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
